﻿using StarPizza.Model.BaseModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StarPizza.Model.User
{
    public enum AuthenticationState
    {
        Off,
        On,
        Registrating
    }
    public class UserSpace : IUserModeManager
    {
        StarDataDataContext db;
        ManagerFactory managerFactory;
        ChefFactory chefFactory;

        public delegate void AuthenticationStateChangedEventHandler(AuthenticationState newState);
        public event AuthenticationStateChangedEventHandler AuthenticationStateChanged;

        public event UserModeChangedEventHandler UserModeChanged;
        public event Action<Client> ClientRegistrated;

        public UserSpace(StarDataDataContext db)
        {
            this.db = db;
            AuthenticationState = AuthenticationState.Off;
            UserMode = UserMode.Guest;
            managerFactory = new ManagerFactory();
            managerFactory.EnsureUser(db);
            chefFactory = new ChefFactory();
            chefFactory.EnsureUser(db);
        }
        public List<Client> Clients
        {
            get { return db.Client.ToList(); }
        }

        public Client LoggedInClient { get; private set; }
        
        private AuthenticationState authenticationState;
        public AuthenticationState AuthenticationState
        {
            get { return authenticationState; }
            private set
            {
                if (authenticationState != value)
                {
                    authenticationState = value;
                    if (AuthenticationStateChanged != null)
                        AuthenticationStateChanged(authenticationState);
                }
            }
        }
        
        UserMode userMode;
        public UserMode UserMode
        {
            get { return userMode; }
            private set
            {
                if (userMode != value)
                {
                    userMode = value;
                    if (UserModeChanged != null)
                        UserModeChanged(userMode);
                }
            }
        }
    
        private BaseUser GetBaseUserIfAny(string login)
        {
            var foundBaseUsers = db.BaseUser.Where(user => login == user.Login);
            if (foundBaseUsers.Any())
            {
                return foundBaseUsers.First();
            }
            else
            {
                return null;
            }
        }
        private void HandleUserLogin(BaseUser baseUser)
        {
            UserMode prevMode = UserMode;
            if (baseUser.Client != null)
            {
                if (!baseUser.Client.IsAllowed)
                {
                    throw new Exception("User is blocked");
                }
                else
                {
                    LoggedInClient = baseUser.Client;
                    UserMode = UserMode.Client;
                }
            }
            else if (managerFactory.Login == baseUser.Login)
            {
                UserMode = UserMode.Manager;
            } 
            else if (chefFactory.Login == baseUser.Login)
            {
                UserMode = UserMode.Chef;
            }
            else
            {
                throw new Exception("We have a serious problem: the user is not a Client, nor a manager or chef but exists in the database");
            }
        }
        public void Authenticate(string login, string encryptedPassword)
        {
            var baseUser = GetBaseUserIfAny(login);
            if (baseUser == null)
            {
                throw new Exception("There is no user with this login: " + login);
            }
            if (baseUser.Password != encryptedPassword)
            {
                throw new Exception("Incorrect password");
            }
            HandleUserLogin(baseUser);
            AuthenticationState = AuthenticationState.On;
        }
        public void StartRegistration()
        {
            AuthenticationState = AuthenticationState.Registrating;
        }
        public void CancelRegistration()
        {
            AuthenticationState = AuthenticationState.Off;
        }
        public void LogOff()
        {
            LoggedInClient = null;
            AuthenticationState = AuthenticationState.Off;
            UserMode = UserMode.Guest;
        }
        public void RegistrateClient(string login, string encryptedPassword, 
            string name, int district, string address)
        {
            if (UserFactory.BaseUserExists(db, login))
            {
                throw new Exception("This login is not available!");
            }
            else
            {
                var baseUser = UserFactory.ProvideBaseUser(db, login, encryptedPassword);
                var client = new Client
                {
                    BaseUser = baseUser,
                    Name = name,
                    District = district,
                    Address = address,
                    IsAllowed = true
                };
                db.Client.InsertOnSubmit(client);
                Utility.TrySubmitChanges(db);
                if (ClientRegistrated != null)
                    ClientRegistrated(client);
            }
        }
    }
}
